package xj.graph2d.shapes;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Map;

import javax.swing.Icon;

import xj.graph2d.Arrow;
import xj.graph2d.DrawAttr;
import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeConstants;
import xj.graph2d.DrawShapeFactory;
import xj.graph2d.GraphComponentShape;
import xj.graph2d.OpenShape;
import xj.graph2d.TextShape;

import org.jvnet.flamingo.common.icon.ResizableIcon;

/**
 * An icon represents a scaled image of the shape.
 */
public class ShapeIcon 
  implements ResizableIcon {

  public ShapeIcon() {
  }

  public ShapeIcon(DrawShape shape) {
    this(shape, 16, 16);
  }

  public ShapeIcon(DrawShape shape, int width, int height) {
    setShapeIcon(shape, width, height);
  }

  public void setShapeIcon(DrawShape shape) {
    setShapeIcon(shape, iconWidth, iconHeight);
  }

  public void setShapeIcon(DrawShape shape0, int width, int height) {
    iconWidth = width;
    iconHeight = height;
    dim = new Dimension(width, height);
    if (shape0 != null) {
      this.shape = getIconInstance(shape0);
      if (debug) {
	System.out.println("ShapeIcon(): shape=" + shape0);
	System.out.println("ShapeIcon(): @1 this.shape=" + this.shape);
      }

      this.shape.calculateBounds();
      Rectangle2D bounds = this.shape.getBounds();
      float swidth = (float) bounds.getWidth();
      float sheight = (float) bounds.getHeight();
      float sx = (width - 2) / swidth;
      float sy = (height - 2) / sheight;
      float s = Math.min(sx, sy);
      shapeWidth = (int) (swidth * s);
      shapeHeight = (int) (sheight * s);
      // scale the shape
      float ax1 = 0, ay1 = 0, ax2 = shapeWidth, ay2 = shapeHeight;
      float x1, x2, y1, y2; 
      if (this.shape instanceof OpenShape) {
	x1 = (float) this.shape.getEnd1().getX();
	y1 = (float) this.shape.getEnd1().getY();
	x2 = (float) this.shape.getEnd2().getX();
	y2 = (float) this.shape.getEnd2().getY();
	if (x1 > x2) {
	  ax1 = shapeWidth;
	  ax2 = 0;
	}
	if (y1 > y2) {
	  ay1 = shapeHeight;
	  ay2 = 0;
	}
      }
      this.shape.scaleEnds(ax1, ay1, ax2, ay2);

      dx = (iconWidth - shapeWidth) / 2;
      dy = (iconHeight - shapeHeight) / 2;

      if (debug) {
	if (this.shape instanceof CubicCurveShape) { 
	  System.out.println("ShapeIcon(): @2 this.shape=" + this.shape);
	  System.out.println("  ShapeIcon(): @2 : " + shapeWidth + "x" + shapeHeight + 
			     " [" + dx + "," + dy + "] (" + ax1 + ","+ ay1 + ","+ ax2 + ","+ ay2 + ")");
	}
      }

      DrawAttr attr = this.shape.getDrawAttr();
      if (attr != null) {
	int penSize = attr.getPenSize();
	if (penSize > 1) {
	  penSize = (int) (penSize * s);
	  if (penSize == 0) {
	    penSize = 1;
	  }
	  attr.setPenSize(penSize);
	}
      }

      if (this.shape instanceof OpenShape) {
	OpenShape oshape = (OpenShape) this.shape;
	Arrow headArrow = oshape.getHeadArrow();
	Arrow tailArrow = oshape.getTailArrow();
	if (headArrow != null) {
	  float arrowSize = headArrow.getSize();
	  arrowSize = arrowSize * s;
	  if (arrowSize < 5) {
	    arrowSize = 5;
	  }
	  headArrow.setSize(arrowSize);
	}
	if (tailArrow != null) {
	  float arrowSize = tailArrow.getSize();
	  arrowSize = arrowSize * s;
	  if (arrowSize < 5) {
	    arrowSize = 5;
	  }
	  tailArrow.setSize(arrowSize);
	}
      } else if (this.shape instanceof TextShape) {
	TextShape text = (TextShape) this.shape;
	String str = text.getString();
	if (str == null || str.length() == 0) {
	  str = text.getPrompt();
	  text.setString(str);
	}
      }

      if (debug) {
	System.out.println("ShapeIcon(): @3 this.shape=" + this.shape);
      }

    }
  }

  public void setDimension(Dimension newDimension) {
    if (newDimension != null) { 
      dim = newDimension;
      iconWidth = dim.width;
      iconHeight = dim.height;
    }
  }

  public void paintIcon(Component c, Graphics g, int x, int y) {
    if (debug) {
      System.out.println("ShapeIcon.paintIcon() @1: shape=" + shape);
    }

    if (shape != null && iconWidth > 0 && iconHeight > 0) {
      Graphics2D g2 = (Graphics2D) g;
      Color oldColor = g2.getColor();
      Font oldFont = g2.getFont();
      g2.setColor(Color.black);
      shape.move(x + dx, y + dy);
      shape.setShadow(false, true);
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
			  RenderingHints.VALUE_ANTIALIAS_ON);
      shape.drawShape(g2);
      shape.move(- x - dx, - y - dy);
      g2.setColor(oldColor);
      g2.setFont(oldFont);

      if (debug) {
	if (shape instanceof PolyCurveShape) {
	  System.out.println("ShapeIcon.paintIcon() @2: shape=" + shape);
	}
      }
    }

    if (debug) {
      System.out.println("ShapeIcon.paintIcon() @3: exit");
    }
  }

  public int getIconWidth() {
    return dim.width;
  }

  public int getIconHeight() {
    return dim.height;
  }

  public DrawShape getDrawShape() { 
    return shape;
  }

  public DrawAttr getDrawAttr() { 
    if (shape != null) { 
      return shape.getDrawAttr();
    }
    return null;
  }

  protected int shapeWidth, shapeHeight;

  protected int iconWidth, iconHeight;

  protected Dimension dim;

  protected DrawShape shape;

  protected int dx;

  protected int dy;

  static protected Map instanceMap = new HashMap();

  static protected DrawShape getIconInstance(DrawShape proto) {
    DrawShape shape = null;
    if (proto != null) {
      /*
      String classname = proto.getClass().getName();
      Object o = instanceMap.get(classname);
      if (o instanceof DrawShape) {
	shape = (DrawShape) o;
      } else {
	shape = cloneInstance(proto, true);
      }
      */
      shape = cloneInstance(proto, true);
      DrawShapeFactory.cloneDrawAttr(proto, shape, null,
				     DrawShapeConstants.CLONE_PROTOTYPE_DRAW_ATTR);
    }
    return shape;
  }

  static protected DrawShape cloneInstance(DrawShape proto, boolean nocomp) {
    DrawShape s = null;
    if (proto != null) {
      try { 
	// nocomp: do not clone the comp of a graph component
	if (nocomp && 
	    proto instanceof GraphComponentShape) {
	  GraphComponentShape gc = (GraphComponentShape) proto;
	  if (gc instanceof GraphEdgeShape) { 
	    GraphEdgeShape e = (GraphEdgeShape) gc; 
	    gc = e.getEdgeShape();
	  }
	  boolean full = gc.isFullCloneMode();
	  boolean clabel = gc.isCloneLabel();
	  gc.setFullCloneMode(false);
	  gc.setCloneLabel(false);
	  s = (DrawShape) gc.clone();
	  gc.setFullCloneMode(full);
	  gc.setCloneLabel(clabel);
	} else {
	  s = (DrawShape) proto.clone();
	}
      } catch (CloneNotSupportedException e) {
      }
    }
    return s;
  }

  static protected final boolean debug = false;

}
